import serial
import time
import sys
import bitstring
import fastCRC
import math
from logilab.common.compat import raw_input
from serial.serialutil import SerialException
from string import upper
from bitstring import BitStream

debug = False
initialized = False
runStatus = False
bitfieldMap = "uint:8,8,3,3,1,1,8,8"
maxPayloadLength = 16  # #value for useful serialStatusging. Change to 256 when done

############# Menu Options #############
'''q

	# options 
	# Run - 
	# Stop - 
	# Display a message - Echo a string back from MCU
	# transmit mode - enter destination 
		# string
		# file
	# receive 
		# print to screen
		# print to file
	# Exit
	'''
# def main(argv):	
# try:
# 	opts, args = getopt(argv, "hp:b", ["ttyPort=", "baud="])
# except GetoptError:
# 	print "mcuHost.py -p <ttyPath> -b <baudrate>"
# 		sys.exit(-1)
	

serialStatus = True
if (debug == False):
	ser = serial.Serial()
	
	ser.port = "/dev/ttyACM0"
	ser.baudrate = 115200
	ser.timeout = .3
	try:
		ser.open()
	except SerialException:	
		print 'unable to open serial port' , ser.port
		sys.exit(1)
	try:	
		ser.flushInput()  
		ser.flushOutput()
		print 'flushed serial IO'
	except SerialException:
		print 'problem flushing the serial buffer'
	ser.close()
	ser.open()	
	print 'serial open state:', serialStatus
	time.sleep(0.2)  # #necesary for arduino board unless "auto-reboot on new serial session" is bypassed in hardware
	mcuResponse = ser.read(3)
	print 'MCU response', mcuResponse
	if (mcuResponse == '$B<'):
		print '--MCU booted successfully!'

############---------Menu Code Begin ----------##############
menuInput = '!'  # dummy data to instatiate input object
while (menuInput != 'Q'):
	
	if (initialized == False):
		ser.write('$I')
		mcuResponse = ser.read(3)
		print 'MCU response', mcuResponse
		if (mcuResponse == '$I<') and (initialized == False):
			print '--MCU init success!'
			initialized = True
	print '\n###### MCU host control script ######\n\tChoose one of the following options:'
	print ' (R) Run  - Send run command to IO Task.'
	print ' (S) Stop - Send Stop commandto IO task'
	print ' (D) Display a message - Echo test string and CRC from MCU'
	print ' (T) Transmit - transmit a string or file'
	print ' (X) Receive a string or file.'
	print ' (Q) Quit - End all MCU activity and exit this script '

	
		
	while menuInput not in ('R', 'D', 'T', 'S', 'X', 'Q') :
		menuInput = raw_input('Enter Mode selection:  ')
		menuInput = menuInput.upper()
		if menuInput not in ('R', 'D', 'T', 'X', 'S', 'Q'):
			print 'Enter one of:\'R\' \'D\' \'T\' \'X\' \'S\' \'Q\''
			
##################---------- Run Block-------------------###################
	if (menuInput == 'R'):	
		if (runStatus == True):
			print 'Already Running!'
		else:
			ser.write('$R')
			if (ser.read(3) == '$R<'):
				print "--MCU acknowledged Run Command"
				runStatus = True
		menuInput = '0'


###########################----------- Stop State -----------###############
	elif (menuInput == 'S'):
		if (runStatus == False):
			print 'Already stopped!'
		else:
			ser.write('$S')
			if (ser.read(3) == '$S<'):
				print "--MCU acknowledged stop Command"
				runStatus = False
		menuInput = '0'
		
		
###################### Display Block ##################
	elif (menuInput == 'D'):  #
		if (runStatus == True):
			ser.write('$D')
			ser.timeout = 1
			mcuResponse = ser.read(3)
			print '\n--MCU response to display command', mcuResponse
			if (mcuResponse == '$D<'):
				print '--MCU acknowledges DisplayMode'
				displaymessage = raw_input('\n\n Enter a message to display: ')
				ser.write(displaymessage + '\r')
				print 'String to write:', displaymessage
			
				print 'target echoed:' , ser.read(len(displaymessage))
				localMessageCrc = fastCRC.stringCrc16(displaymessage)
	
				mcuMessageCrc = ser.read(6)
				print 'computed crc=' , upper(hex(localMessageCrc))
				print 'mcu calc crc=', mcuMessageCrc  # .encode('hex')
				if (mcuMessageCrc == upper(hex(localMessageCrc))):
					print 'CRC Match'
				else:
					'CRC error'
				dummyobject = raw_input('press enter to return to menu')
		else:
			print '\nMCU must be in running state to display a message\n'
		menuInput = '0'	
		
			
	
#### --Quit command ################################################
	elif (menuInput == 'Q'):  
		print '\nSending shutdown command to MCU.'
		ser.timeout = 0.5
		ser.write('$Q')
# 		time.sleep(.5)
		mcuResponse = ser.read(3)
		if (mcuResponse == '$Q<'):
			print '--Quit command echoed by MCU.'
			
		try: 
			ser.close()
			print 'Serial port closed.'
		except serial.SerialException:
			print 'There was a problem closing the port.'
		print 'Exiting now'
		sys.exit()
####   Reciever Block	###########################################
	elif (menuInput == 'X'):
		print 'not yet implemented'
		menuInput = '0'
		
		
		
		
####  Transmitter Block #########################################
	elif (menuInput == 'T'):  
		ser.write('T')
		ser.setTimeout(1)
		mcuResponse = ser.read(3)
		if (mcuResponse == '$T<'):
			print '--Transmit Mode command echoed by MCU.--'
		
		#############  Define the data to be transmitted  #############
		# #  8,8,3,3,1,1,8,8
		src = 0x0  # #define own machine address
		dst = 4  # raw_input('\n' + "Enter target address (0-255): ")
		ns = 0
		nr = 0
		rsv = 0
		lst = 0
		opCode = 0x00
		length = 0
		message = '"Lorem ipsum dolor sit sed do eiusmod amet, sed do eiusmod consectetur adipisicing ipsum dolor sit amet elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua'  # dummy string to simplify testing
# 		message = raw_input('\n' + "Enter a string (>256) to transmit: ")  # #on
		length = len(message) 
		print 'message to be transmitted', message, '\nmessage length =', length
		print 'source =', hex(src), '\ndst =', dst
		
		
		###------   Calculate and Assemble Packet   ---##############
		"""packetCount is modulus of bitfield length in
		  bytes, playload size, and two bytes for crc"""  
		packetCount = int(math.ceil(float(length) / float(maxPayloadLength)))  # find minimum number of packets to transmit message
		print 'packetcount=', packetCount, '\n----------\n\n'
			
			
		
		for iterationNum in range (packetCount):
			'''
			#------ program flow and To do list ------ 
			# x Make header
			# x Make packetpayload
			# x Make then append crc
			# x Transmit payload
			# x Read MCU packet header for response '''
			if (iterationNum < packetCount - 1):
				'''
				# Grab full 256 byte string chunks
				# the slice code for the payload below, will fetch maxPayloadLength number of bits
				# per iteration
				# '''
				print '\npacket ID=', iterationNum
				endByte = maxPayloadLength * (iterationNum + 1) 
				PayloadLength = 0
			else:
				# create header for variable length packet
				print '\nlast packet'
				lst = 1
				endByte = maxPayloadLength * (iterationNum) + length % maxPayloadLength
				PayloadLength = length % maxPayloadLength
			
				
			
			
		###########  Packet transaction  ###############	
			

			PacketHdr = bitstring.pack(bitfieldMap, src, dst, iterationNum % 8, nr, rsv, lst, opCode, PayloadLength)  # actually length 256. But lst==0!
			startByte = maxPayloadLength * iterationNum
			payload = message[startByte:endByte]
			crc = fastCRC.stringCrc16(PacketHdr.bytes + payload)
			
 			ser.write(PacketHdr.bytes + payload)
   			ser.write(chr((crc & 0xff00) >> 8) + chr((crc & 0xff)))

			print 'payload =', payload
			print 'sending bytes ', startByte, 'through', endByte
# 			print 'header crc=  ', hex(fastCRC.stringCrc16(PacketHdr.bytes))
			print 'Expected CRC:', hex(crc) 
			mcuCrc = '0x' + ser.read(2).encode('hex')
			print 'Recieved CRC:', mcuCrc
			mcuHeaderRx = ser.read(5)
			print 'transmit packetHDR: 0x' + PacketHdr.hex			
			print 'Received packetHDR: 0x' + mcuHeaderRx.encode('hex')
		
			mcuHeader = BitStream(bytes=mcuHeaderRx)

			
			mcusrc, mcudst, mcuns, mcunr, mcursv, mculst, mcuopCode, mculength = mcuHeader.readlist(bitfieldMap) 
			
			print '\n-----------'
			print '          Src\tDest.\t#sent\t#rec\trsv\tlast\topCode\tlen'
			print 'Original:', src, '\t', dst, '\t', iterationNum % 8, '\t', nr, '\t', rsv, '\t', lst, '\t', opCode, '\t', PayloadLength
			print 'received:', mcusrc, '\t', mcudst, '\t', mcuns, '\t', mcunr, '\t', mcursv, '\t', mculst, '\t', mcuopCode, '\t', mculength
			print "leftover bytes>" + ser.read(ser.inWaiting()) + "<"	
			
		ser.flushInput()
		ser.flushOutput()
		sys.exit()
		menuInput = '!'
		print '\n\nTransmission complete. \nPress enter to continue'
		
		
